﻿Imports System.Reflection
Imports System.ComponentModel.DataAnnotations

Partial Public Class ExcelExporter(Of T)

    Private Class ExportTreeNode
        Property ID As Guid
        Property PropertyInfo As PropertyInfo
        Property MemberAtt As ExportMemberAttribute
        Property Nodes As New List(Of ExportTreeNode)
    End Class

    Private Class ExportTreeNodeComparer
        Implements IComparer(Of ExportTreeNode)

        Public Function Compare(x As ExcelExporter(Of T).ExportTreeNode, y As ExcelExporter(Of T).ExportTreeNode) As Integer Implements System.Collections.Generic.IComparer(Of ExcelExporter(Of T).ExportTreeNode).Compare
            If x.MemberAtt.Order > y.MemberAtt.Order Then
                Return 1
            ElseIf x.MemberAtt.Order < y.MemberAtt.Order Then
                Return -1
            Else
                Return x.MemberAtt.ColumnName.CompareTo(y.MemberAtt.ColumnName)
            End If
        End Function
    End Class

    Private Function RecursiveTreeMaxDepth(node As ExportTreeNode, lastdepth As Integer) As Integer
        If node.Nodes.Count = 0 Then
            Return lastdepth
        Else
            Return Aggregate n In node.Nodes Let d = RecursiveTreeMaxDepth(n, lastdepth + 1) Into Max(d)
        End If
    End Function

    Private Function RecursiveExportTree(parent As ExportTreeNode, bType As Type, Optional mTypeAtt As MetadataTypeAttribute = Nothing) As ExportTreeNode
        Dim rType As Type
        If mTypeAtt Is Nothing Then rType = bType Else rType = mTypeAtt.MetadataClassType

        For Each prop As PropertyInfo In rType.GetProperties

            For Each p As Object In prop.GetCustomAttributes(False)
                If TypeOf p Is ExportMemberAttribute Then
                    Dim cMapAtt As ExportMemberAttribute = p
                    Dim etn As New ExportTreeNode With {.ID = cMapAtt.ID, .MemberAtt = cMapAtt}

                    If mTypeAtt Is Nothing Then
                        etn.PropertyInfo = prop
                    Else
                        If bType.GetProperty(prop.Name) Is Nothing Then Throw New Exception("MetadataType mismatch """ & prop.Name & """")
                        etn.PropertyInfo = bType.GetProperty(prop.Name)
                    End If

                    If etn.MemberAtt.BackgroundColor = "" Then etn.MemberAtt.BackgroundColor = parent.MemberAtt.BackgroundColor
                    If etn.MemberAtt.ForegroundColor = "" Then etn.MemberAtt.ForegroundColor = parent.MemberAtt.ForegroundColor
                    If etn.MemberAtt.BorderColor = "" Then etn.MemberAtt.BorderColor = parent.MemberAtt.BorderColor
                    If etn.MemberAtt.BorderLineStyle = XlLineStyle.Undefined Then etn.MemberAtt.BorderLineStyle = parent.MemberAtt.BorderLineStyle
                    If etn.MemberAtt.BorderWeight = XlBorderWeight.Undefined Then etn.MemberAtt.BorderWeight = parent.MemberAtt.BorderWeight
                    If etn.MemberAtt.HAlign = XlHAlign.Undefined Then etn.MemberAtt.HAlign = parent.MemberAtt.HAlign
                    If etn.MemberAtt.VAlign = XlVAlign.Undefined Then etn.MemberAtt.VAlign = parent.MemberAtt.VAlign


                    If prop.PropertyType.GetProperties.Count > 0 Then
                        etn = RecursiveExportTree(etn, prop.PropertyType)
                    End If

                    parent.Nodes.Add(etn)
                End If
            Next
        Next

        If parent.Nodes.Count > 0 Then
            parent.Nodes.Sort(New ExportTreeNodeComparer)
        End If

        Return parent
    End Function
End Class
